<?php

/** 
 * Pagination Helper, responsible for managing the LINKS required for pagination. 
 * ALL parameters are specified in the component. 
 */
class PaginationHelper extends Helper
{
    /* 
 * Options to be passed to ajax links if used 
 * 
 * @var array 
 * @access public 
 */
  public $ajaxLinkOptions = array();
/* 
 * Placeholder for the link style - defined in/by the component 
 * 
 * @var boolean 
 * @access private 
 */
  public $style = 'html';
/* 
 * Placeholder for the parameter style - defined in/by the component 
 * 
 * @var boolean 
 * @access private 
 */
  public $paramStyle = 'get';
/* 
 * Placeholder for the pagination details 
 * 
 * @var array 
 * @access private 
 */
  public $_pageDetails = array();
/* 
 * Helpers 
 * 
 * @var array 
 * @access private 
 */
  public $helpers = array('Html','Ajax');

  /** 
   * Sets the default pagination options. Fails if the value $paging is not set.
   * 
   * @param array $paging an array detailing the page options 
   *
   * @return bool 
   */
  public function setPaging($paging)
  {
      if (empty($paging)) {
          return false;
      }
      $this->_pageDetails = $paging;
      $this->_pageDetails['previousPage'] = ($paging['page'] > 1) ? $this->_pageDetails['page'] - 1 : '';
      $this->_pageDetails['nextPage'] = ($paging['page'] < $this->_pageDetails['pageCount']) ? $this->_pageDetails['page'] + 1 : '';

      $this->url = $this->_pageDetails['url'];

      $getParams = $this->params['url'];
      unset($getParams['url']);
      $this->getParams = $getParams;

      $this->showLimits = $this->_pageDetails['showLimits'];
      $this->style = isset($this->_pageDetails['style']) ? $this->_pageDetails['style'] : $this->style;

      if (($this->_pageDetails['maxPages'] % 2) == 0) {
          // need odd number of page links 

      $this->_pageDetails['maxPages'] = $this->_pageDetails['maxPages'] - 1;
      }

      $this->maxPages = $this->_pageDetails['maxPages'];
      $this->pageSpan = ($this->_pageDetails['maxPages'] - 1) / 2;

      return true;
  }

  /** 
   * Displays the list of pages for the given parameters. 
   * 
   * @param string text to display before limits 
   * @param string display a separate between limits 
   * @param bool whether to escape the title or not 
   *
   * @return unknown the html string for modifying the number of results per page 
   **/
  public function resultsPerPage($t = 'Results per page: ', $separator = ' ', $escapeTitle = true)
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      $pageDetails = $this->_pageDetails;
      if (!empty($pageDetails['pageCount'])) {
          if (is_array($pageDetails['resultsPerPage'])) {
              $OriginalValue = $pageDetails['show'];
              $t .= $separator;
              foreach ($pageDetails['resultsPerPage'] as $value) {
                  if ($OriginalValue == $value) {
                      $t .= '<em>'.$value.'</em>'.$separator;
                  } else {
                      $pageDetails['show'] = $value;
                      $t .= $this->_generateLink($value, 1, $escapeTitle, $pageDetails).$separator;
                  }
              }
          }

          return $t;
      }

      return false;
  }

  /** 
   * Generate a select box for options for results per page.
   * 
   * @param string prefix text 
   *
   * @return unknown the html string for the select box for modifying the number of results per page 
   **/
  public function resultsPerPageSelect($t = 'Results per page: ')
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if (!empty($this->_pageDetails['pageCount'])) {
          $Options = array();
          if (is_array($this->_pageDetails['resultsPerPage'])) {
              foreach ($this->_pageDetails['resultsPerPage'] as $value) {
                  $Options[$value] = $value;
              }
          }

          return $t.$this->Html->selectTag('pagination/show', $Options, $this->_pageDetails['show'], null, null, false);
      }

      return false;
  }

  /** 
   * Displays info of the current result set.
   * 
   * @param string prefix text 
   * @param string 
   * @param string 
   *
   * @return unknown the html string for the current result set. 
   **/
  public function result($t = 'Results: ', $of = ' of ', $inbetween = '-')
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if (!empty($this->_pageDetails['pageCount'])) {
          if ($this->_pageDetails['pageCount'] > 1) {
              $start_row = (($this->_pageDetails['page'] - 1) * $this->_pageDetails['show']) + 1;
              $end_row = min((($this->_pageDetails['page']) * $this->_pageDetails['show']), ($this->_pageDetails['total']));
              $t = $t.$start_row.$inbetween.$end_row.$of.$this->_pageDetails['total'];
          } else {
              $t .= $this->_pageDetails['total'];
          }

          return $t;
      }

      return false;
  }
  /** 
   * Returns a list of page numbers separated by $separator.
   * 
   * @param string $separator - defaults to null 
   * @param bool 
   * @param string $spacerLower - If there are more results than space for the links, the text inbetween 
   * @param string $spacerUpper - If there are more results than space for the links, the text inbetween 
   *
   * @return string html for the list of page numbers 
   **/
  public function pageNumbers($separator = null, $escapeTitle = true, $spacerLower = '...', $spacerUpper = '...')
  {
      if (empty($this->_pageDetails) || $this->_pageDetails['pageCount'] == 1) {
          return '<b>1</b>';
      }
      $total = $this->_pageDetails['pageCount'];
      $max = $this->maxPages;
      $span = $this->pageSpan;
      if ($total < $max) {
          $upperLimit = min($total, ($span * 2 + 1));
          $lowerLimit = 1;
      } elseif ($this->_pageDetails['page'] < ($span + 1)) {
          $lowerLimit = 1;
          $upperLimit = min($total, ($span * 2 + 1));
      } elseif ($this->_pageDetails['page'] > ($total - $span)) {
          $upperLimit = $total;
          $lowerLimit = max(1, $total - $span * 2);
      } else {
          $upperLimit = min($total, $this->_pageDetails['page'] + $span);
          $lowerLimit = max(1, ($this->_pageDetails['page'] - $span));
      }

      $t = array();
      if (($lowerLimit != 1) and ($this->showLimits)) {
          $lowerLimit = $lowerLimit + 1;
          $t[] = $this->_generateLink(1, 1, $escapeTitle);
          if ($spacerLower) {
              $t[] = $spacerLower;
          }
      }
      if (($upperLimit != $total) and ($this->showLimits)) {
          $dottedUpperLimit = true;
      } else {
          $dottedUpperLimit = false;
      }
      if (($upperLimit != $total) and ($this->showLimits)) {
          $upperLimit = $upperLimit - 1;
      }
      for ($i = $lowerLimit; $i <= $upperLimit; ++$i) {
          if ($i == $this->_pageDetails['page']) {
              $text = '<b>'.$i.'</b>';
          } else {
              $text = $this->_generateLink($i, $i, $escapeTitle);
          }
          $t[] = $text;
      }
      if ($dottedUpperLimit) {
          if ($spacerUpper) {
              $t[] = $spacerUpper;
          }
          $t[] = $this->_generateLink($this->_pageDetails['pageCount'], $this->_pageDetails['pageCount'], $escapeTitle);
      }
      $t = implode($separator, $t);

      return $t;
  }

  /** 
   * Displays a link to the previous page, where the page doesn't exist then 
   * display the $text.
   * 
   * @param string $text - text display: defaults to next 
   *
   * @return string html for link/text for previous item 
   **/
  public function prevPage($text = 'prev', $escapeTitle = true, $linkTitle = null)
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if (!empty($this->_pageDetails['previousPage'])) {
          return $this->_generateLink($text, $this->_pageDetails['previousPage'], $escapeTitle, null, $linkTitle);
      }

      return $text;
  }

  /** 
   * Displays a link to the next page, where the page doesn't exist then 
   * display the $text.
   * 
   * @param string $text - text to display: defaults to next 
   *
   * @return string html for link/text for next item 
   **/
  public function nextPage($text = 'next', $escapeTitle = true, $linkTitle = null)
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if (!empty($this->_pageDetails['nextPage'])) {
          return $this->_generateLink($text, $this->_pageDetails['nextPage'], $escapeTitle, null, $linkTitle);
      }

      return $text;
  }

  /** 
   * Displays a link to the first page 
   * display the $text.
   * 
   * @param string $text - text to display: defaults to next 
   *
   * @return string html for link/text for next item 
   **/
  public function firstPage($text = 'first', $escapeTitle = true, $linkTitle = null)
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if ($this->_pageDetails['page'] != 1) {
          return $this->_generateLink($text, 1, $escapeTitle, null, $linkTitle);
      } else {
          return false;
      }
  }

  /** 
   * Displays a link to the last page 
   * display the $text.
   * 
   * @param string $text - text to display: defaults to next 
   *
   * @return string html for link/text for next item 
   **/
  public function lastPage($text = 'last', $escapeTitle = true, $linkTitle = null)
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if ($this->_pageDetails['page'] != $this->_pageDetails['pageCount']) {
          return $this->_generateLink($text, $this->_pageDetails['pageCount'], $escapeTitle, null, $linkTitle);
      } else {
          return false;
      }
  }

  /** 
   * Generate link to sort the results by the given value.
   * 
   * @param string field to sort by 
   * @param string title for link defaults to $value 
   * @param string model to sort by - uses the default model class if absent 
   * @param bool escape title 
   * @param string text to append to links to indicate sorted ASC 
   * @param string text to append to links to indicate sorted DESC 
   *
   * @return string html for link to modify sort order 
   **/
  public function sortBy($value, $title = null, $Model = null, $escapeTitle = true, $upText = ' ^', $downText = ' v')
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      $title = $title ? $title : ucfirst($value);
      $value = strtolower($value);
      $Model = $Model ? $Model : $this->_pageDetails['Defaults']['sortByClass'];

      $OriginalSort = $this->_pageDetails['sortBy'];
      $OriginalModel = $this->_pageDetails['sortByClass'];
      $OriginalDirection = $this->_pageDetails['direction'];

      if (($value == $OriginalSort) && ($Model == $OriginalModel)) {
          if (up($OriginalDirection) == 'DESC') {
              $this->_pageDetails['direction'] = 'ASC';
              $title .= $upText;
          } else {
              $this->_pageDetails['direction'] = 'DESC';
              $title .= $downText;
          }
      } else {
          if ($Model) {
              $this->_pageDetails['sortByClass'] = $Model;
        //echo "page details model class set to ".$this->_pageDetails['sortByClass']."<br>"; 
          } else {
              $this->_pageDetails['sortByClass'] = null;
          }
          $this->_pageDetails['sortBy'] = $value;
      }
      $link = $this->_generateLink($title, 1, $escapeTitle);
      $this->_pageDetails['sortBy'] = $OriginalSort;
      $this->_pageDetails['sortByClass'] = $OriginalModel;
      $this->_pageDetails['direction'] = $OriginalDirection;

      return $link;
  }

  /** 
   * Generate a select box for options to sort results.
   * 
   * @param array array of text strings, formatted as "Field::Direction::Class". 
   * @param string prefix text 
   * @param string text to append to links to indicate sorted ASC 
   * @param string text to append to links to indicate sorted DESC 
   *
   * @return unknown the html string for the select box for selecting sort order 
   **/
  public function sortBySelect($sortFields, $t = 'Sort By: ', $upText = ' ^', $downText = ' v')
  {
      if (empty($this->_pageDetails)) {
          return false;
      }
      if (!empty($this->_pageDetails['pageCount'])) {
          $OriginalValue = $this->_pageDetails['sortBy'].'::'.$this->_pageDetails['direction'].'::'.$this->_pageDetails['sortByClass'];
          if (is_array($sortFields)) {
              foreach ($sortFields as $value) {
                  $Vals = array();
                  $Vals = explode('::', $value);
                  if (isset($Vals[2])) {
                      $DisplayVal = $Vals[2].' ';
                  } else {
                      $DisplayVal = '';
                  }
                  $DisplayVal .= $Vals[0];
                  if (up($Vals[1]) == 'ASC') {
                      $DisplayVal .= $downText;
                  } else {
                      $DisplayVal .= $upText;
                  }
                  $Options[$value] = $DisplayVal;
              }

              return $t.$this->Html->selectTag('pagination/sortByComposite', $Options, $OriginalValue, null, null, false);
          }
      }

      return false;
  }

  /** 
   * Internal method to generate links based upon passed parameters. 
   * 
   * @param string title for link 
   * @param string page the page number 
   * @param bool escape title 
   * @param string the div to be updated by AJAX updates 
   *
   * @return string html for link 
   **/
  public function _generateLink($title, $page = null, $escapeTitle, $pageDetails = null, $linkTitle = null)
  {
      $pageDetails = $pageDetails ? $pageDetails : $this->_pageDetails;
      $url = $this->_generateUrl($page, $pageDetails);
      $AjaxDivUpdate = $pageDetails['ajaxDivUpdate'];
      if ($this->style == 'ajax') {
          $options = am($this->ajaxLinkOptions,
              array(
                'update' => $pageDetails['ajaxDivUpdate'],
                )
              );
          if (isset($pageDetails['ajaxFormId'])) {
              $id = 'link'.intval(rand());
              $return = $this->Html->link(
                $title,
                $url,
                array('id' => $id, 'onclick' => ' return false;'),
                null,
                $escapeTitle
                  );
              $options['with'] = "Form.serialize('{$this->_pageDetails['ajaxFormId']}')";
              $options['url'] = $url;
              $return .= $this->Ajax->Javascript->event("'$id'", 'click', $this->Ajax->remoteFunction($options));

              return $return;
          } else {
              return $this->Ajax->link(
                $title,
                $url,
                $options,
                null,
                null,
                $escapeTitle
                  );
          }
      } else {
          return $this->Html->link(
              $title,
              $url,
              array('title' => $linkTitle),
              null,
              $escapeTitle
                );
      }
  }

    public function _generateUrl($page = null, $pageDetails = null)
    {
        $pageDetails = $pageDetails ? $pageDetails : $this->_pageDetails;
        $getParams = $this->getParams; // Import any other pre-existing get parameters 
    if ($this->_pageDetails['paramStyle'] == 'pretty') {
        $pageParams = $pageDetails['importParams'];
    }
        $pageParams['show'] = $pageDetails['show'];
        $pageParams['sortBy'] = $pageDetails['sortBy'];
        $pageParams['direction'] = $pageDetails['direction'];
        $pageParams['page'] = $page ? $page : $pageDetails['page'];
        if (isset($pageDetails['sortByClass'])) {
            $pageParams['sortByClass'] = $pageDetails['sortByClass'];
        }
        $getString = array();
        $prettyString = array();
        if ($pageDetails['paramStyle'] == 'get') {
            $getParams = am($getParams, $pageParams);
        } else {
            foreach ($pageParams as $key => $value) {
                if (isset($pageDetails['Defaults'][$key])) {
                    if (up($pageDetails['Defaults'][$key]) != up($value)) {
                        $prettyString[] = "$key{$pageDetails['paramSeperator']}$value";
                    }
                } else {
                    $prettyString[] = "$key{$pageDetails['paramSeperator']}$value";
                }
            }
        }
        foreach ($getParams as $key => $value) {
            if ($pageDetails['paramStyle'] == 'get') {
                if (isset($pageDetails['Defaults'][$key])) {
                    if (up($pageDetails['Defaults'][$key]) != up($value)) {
                        $getString[] = "$key=$value";
                    }
                } else {
                    $getString[] = "$key=$value";
                }
            } else {
                $getString[] = "$key=$value";
            }
        }
        $url = $this->url;
        if ($prettyString) {
            $prettyString = implode('/', $prettyString);
            $url .= $prettyString;
        }
        if ($getString) {
            $getString = implode('&', $getString);
            $url .= '?'.$getString;
        }

        return $url;
    }
}
